//------------------------------------------------------------------------------
// Copyright (c) 2008 www.dnaide.com
// Licensed under the MIT (MIT-LICENSE.txt)
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.IO;

using DNAide.Core;
using DNAide.Drawing;
using DNAide.Web.UI.Scripts;

namespace DNAide.Web.UI.Controls
{

	/// <summary>
	/// </summary>
	public class ImageText : DNAideImageControl
	{

		private string _clientScriptReplaceHtml = string.Empty;
		private string _textToRender = string.Empty;
		private StringFormat _stringFormat;

		#region Properties

		/// <summary>
		/// </summary>
		public Int32 DrawXPosition
		{
			get { return ((ViewState["DrawXPosition"] == null) ? 0 : (int)ViewState["DrawXPosition"]); }
			set {
				if (value > this.MaxWidth)
				{
					ViewState["DrawXPosition"] = this.MaxWidth;
				}
				else
				{
					ViewState["DrawXPosition"] = value; 
				}
			}
		}

		/// <summary>
		/// </summary>
		public Int32 DrawYPosition
		{
			get { return ((ViewState["DrawYPosition"] == null) ? 0 : (int)ViewState["DrawYPosition"]); }
			set
			{
				if (value > this.MaxHeight)
				{
					ViewState["DrawYPosition"] = this.MaxHeight;
				}
				else
				{
					ViewState["DrawYPosition"] = value;
				}
			}
		}

		/// <summary>
		/// </summary>
		public Int32 ForeColorAlpha
		{
			get { return ((ViewState["ForeColorAlpha"] == null) ? 255 : (int)ViewState["ForeColorAlpha"]); }
			set { ViewState["ForeColorAlpha"] = value; }
		}

		/// <summary>
		/// </summary>
		public Int32 BackColorAlpha
		{
			get { return ((ViewState["BackColorAlpha"] == null) ? 255 : (int)ViewState["BackColorAlpha"]); }
			set { ViewState["BackColorAlpha"] = value; }
		}

		/// <summary>
		/// Text to render
		/// </summary>
		public String Text
		{
			get { return ((ViewState["Text"] == null) ? string.Empty : (string)ViewState["Text"]); }
			set { ViewState["Text"] = value; }
		}

		/// <summary>
		/// Maximum width of the image to generate.
		/// </summary>
		/// <remarks>
		/// Default = 1000px
		/// </remarks>
		public Int32 MaxWidth
		{
			get { return ((ViewState["MaxWidth"] == null) ? BitmapAide.CREATE_TEXT_BITMAP_DEFAULT_MAX_WIDTH : (int)ViewState["MaxWidth"]); }
			set { ViewState["MaxWidth"] = value; }
		}

		/// <summary>
		/// Maximum height of the image to generate.
		/// </summary>
		/// <remarks>
		/// Default = 1000px
		/// </remarks>
		public Int32 MaxHeight
		{
			get { return ((ViewState["MaxHeight"] == null) ? BitmapAide.CREATE_TEXT_BITMAP_DEFAULT_MAX_HEIGHT : (int)ViewState["MaxHeight"]); }
			set { ViewState["MaxHeight"] = value; }
		}

		/// <summary>
		/// Fore color.
		/// </summary>
		/// <remarks>
		/// Default = Black
		/// </remarks>
		public Color ForeColor
		{
			get { return ((ViewState["ForeColor"] == null) ? Color.Black : (Color)ViewState["ForeColor"]); }
			set { ViewState["ForeColor"] = value; }
		}

		/// <summary>
		/// Back color
		/// If you want a transparent color use Color.Transparent.
		/// </summary>
		/// <remarks>
		/// Default = White
		/// </remarks>
		public Color BackColor
		{
			get { return ((ViewState["BackColor"] == null) ? Color.White : (Color)ViewState["BackColor"]); }
			set { ViewState["BackColor"] = value; }
		}

		/// <summary>
		/// Path to ttf font file
		/// </summary>
		/// <remarks>
		/// Can be a url within the site or a file path e.g. ~/fonts/myfont.ttf or c:\images\myfont.ttf
		/// </remarks>
		public String FontFileName
		{
			get { return ((ViewState["FontFileName"] == null) ? string.Empty : (string)ViewState["FontFileName"]); }
			set { ViewState["FontFileName"] = value; }
		}

		/// <summary>
		/// Font family
		/// </summary>
		public String FontFamily
		{
			get { return ((ViewState["FontFamily"] == null) ? "Arial" : (string)ViewState["FontFamily"]); }
			set { ViewState["FontFamily"] = value; }
		}

		/// <summary>
		/// Font size.
		/// </summary>
		/// <remarks>
		/// Default 12px
		/// </remarks>
		public Single FontSize
		{
			get { return ((ViewState["FontSize"] == null) ? 12 : (float)ViewState["FontSize"]); }
			set { ViewState["FontSize"] = value; }
		}

		/// <summary>
		/// Font style.
		/// </summary>
		/// <remarks>
		/// Default = Regular
		/// </remarks>
		public FontStyle FontStyle
		{
			get { return ((ViewState["FontStyle"] == null) ? FontStyle.Regular : (FontStyle)ViewState["FontStyle"]); }
			set { ViewState["FontStyle"] = value; }
		}

		/// <summary>
		/// Font size graphics unit.
		/// </summary>
		/// <remarks>
		/// Default = Pixel
		/// </remarks>
		public GraphicsUnit FontSizeGraphicsUnit
		{
			get { return ((ViewState["FontSizeGraphicsUnit"] == null) ? GraphicsUnit.Pixel : (GraphicsUnit)ViewState["FontSizeGraphicsUnit"]); }
			set { ViewState["FontSizeGraphicsUnit"] = value; }
		}

		
		/// <summary>
		/// String format. Set if you want to change text alignment etc.
		/// </summary>
		/// <remarks>
		/// At the moment, this is not stored in ViewState as StringFormat is not serializable
		/// </remarks>
		public StringFormat StringFormat
		{
			get { return _stringFormat; }
			set { _stringFormat = value; } 
		}
		//public StringFormat StringFormat
		//{
		//	get { return ((ViewState["StringFormat"] == null) ? null : (StringFormat)ViewState["StringFormat"]); }
		//	set { ViewState["StringFormat"] = value; } 
		//}

		/// <summary>
		/// Control render type. 
		/// </summary>
		/// <remarks>
		/// Default = ImageTag
		/// </remarks>
		public TextImageRenderType RenderType
		{
			get { return ((ViewState["RenderType"] == null) ? TextImageRenderType.ImageTag : (TextImageRenderType)ViewState["RenderType"]); }
			set { ViewState["RenderType"] = value; }
		}
		
		/// <summary>
		/// Whether the image clips to the size of the text
		/// </summary>
		/// <remarks>
		/// Default = true
		/// </remarks>
		public Boolean ClipToText
		{
			get { return ((ViewState["ClipToText"] == null) ? true : (bool)ViewState["ClipToText"]); }
			set { ViewState["ClipToText"] = value; }
		}

		/// <summary>
		/// e.g. TextRenderingHint etc.
		/// </summary>
		public GraphicsSettings GraphicsSettings
		{
			get { return ((ViewState["GraphicsSettings"] == null) ? null : (GraphicsSettings)ViewState["GraphicsSettings"]); }
			set { ViewState["GraphicsSettings"] = value; }
		}

		/// <summary>
		/// You can set this for greater font control. If set, the control ignores any other font value
		/// </summary>
		public Font Font
		{
			get { return ((ViewState["Font"] == null) ? null : (Font)ViewState["Font"]); }
			set { ViewState["Font"] = value; }
		}


		/// <summary>
		/// If not OutputFileName is supplied, the control will generate a default file name.
		/// By default this is a name based on the controls Text value. 
		/// </summary>
		public TextImageDefaultOutputFileNameType DefaultOutputFileNameType
		{
			get { return ((ViewState["DefaultOutputFileNameType"] == null) ? TextImageDefaultOutputFileNameType.Text : (TextImageDefaultOutputFileNameType)ViewState["DefaultOutputFileNameType"]); }
			set { ViewState["DefaultOutputFileNameType"] = value; }
		}

		/// <summary>
		/// Allows you to specify the TextRenderingHint i.e. to use ClearType
		/// </summary>
		public TextRenderingHint TextRenderingHint
		{
			get { return ((ViewState["TextRenderingHint"] == null) ? TextRenderingHint.SystemDefault : (TextRenderingHint)ViewState["TextRenderingHint"]); }
			set {
				ViewState["TextRenderingHint"] = value; 
				if (value != TextRenderingHint.SystemDefault)
				{
					if (this.GraphicsSettings == null)
					{
						this.GraphicsSettings = new GraphicsSettings();
					}
					this.GraphicsSettings.TextRenderingHint = value;
				}
			}
		}

		/// <summary>
		/// Allows you to specify replace strings for characters in the text, which will
		/// be replaced for image generation yet kept for rendering the HTML - handy
		/// for keeping sites Xhtml compliant without the need to search for
		/// all Html entities. E.g. "&amp;,&,&reg;[reg]"<br /><br />
		/// A comma delimitered list with search string and replace string.
		/// </summary>
		public String StringReplace
		{
			get { return ((ViewState["StringReplace"] == null) ? null : (String)ViewState["StringReplace"]); }
			set { ViewState["StringReplace"] = value; }
		}


		#endregion

		/// <summary>
		/// </summary>
		protected override void SetFinalUrlAndPath()
		{
			if (string.IsNullOrEmpty(OutputFileName))
			{
				string temp = CastAide.AsString(OutputFileName);
				if (this.DefaultOutputFileNameType == TextImageDefaultOutputFileNameType.Text)
				{
					if (this._textToRender.Length < 1)
					{
						temp = "dnaide-empty-image-text-value";
					}
					temp = UrlAide.MakeUrlSafe(this._textToRender);
				}
				else
				{
					temp = DNAide.Core.StringAide.ToHash(Text, HashAlgorithmType.MD5);

					// remove any / characters that may have been generated by the hash
					temp = temp.Replace("/", String.Empty);
					// remove any \ characters that may have been generated by the hash
					temp = temp.Replace(@"\", String.Empty);
				}
				
				OutputFileName = temp;
			}
			base.SetFinalUrlAndPath();
		}

		/// <summary>
		/// Remove all search strings within the value specified
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		private string ReplaceStrings(string value)
		{
			if (string.IsNullOrEmpty(value))
				return value;

			if (string.IsNullOrEmpty(StringReplace))
				return value;

			string[] split = StringReplace.Split(new char[] { ',' });

			// Ensure an even number of replace strings
			if (split.Length % 2 != 0)
				throw new ApplicationException(Resources.ImageText.StringReplaceInvalid);

			string result = value;

			for (int i = 0; i < split.Length; i = i + 2)
			{
				// Search for the first string and replace with the second
				// in the current replace pair
				result = result.Replace(split[i], split[i + 1]);
			}

			return result;
		}

		/// <summary>
		/// Creates the text image if required
		/// </summary>
		protected override void CreateOutput()
		{
			base.CreateOutput();

			this._textToRender = Text;
			if (RenderType == TextImageRenderType.ClientScriptReplace)
			{
				LiteralControl literalControl;
				if (string.IsNullOrEmpty(Text))
				{
					if (Controls.Count < 1)
					{
						throw new ApplicationException(Resources.ImageText.ClientScriptReplaceError);
					}
					literalControl = (LiteralControl)Controls[0];
					if (literalControl == null)
					{
						throw new ApplicationException(Resources.ImageText.HTMLOnly);
					}
					this._textToRender = StringAide.StripXmlAndTrim(literalControl.Text);
					this._textToRender = ReplaceStrings(this._textToRender);
					_clientScriptReplaceHtml = literalControl.Text;
					if (!_clientScriptReplaceHtml.Contains("##DNAideImageTextId##"))
					{
						_clientScriptReplaceHtml = string.Format(Resources.ImageText.ClientScriptReplaceDefaultWrapperHtml, _clientScriptReplaceHtml);
					}
				}
				else
				{
					_clientScriptReplaceHtml = string.Format(Resources.ImageText.ClientScriptReplaceDefaultWrapperHtml, HttpContext.Current.Server.HtmlEncode(this._textToRender));
				}
				_clientScriptReplaceHtml = _clientScriptReplaceHtml.Replace("##DNAideImageTextId##", ClientID);
			}
			else
			{
				if (string.IsNullOrEmpty(this.AlternateText))
				{
					this.AlternateText = this._textToRender;
				}
			}


			Font font = Font;
			if (Font != null)
			{
				font = Font;
			}
			else
			{
				if (!string.IsNullOrEmpty(FontFileName))
				{
					string fontFileName = FontFileName;
					if (!fontFileName.Contains(@"\"))
					{
						fontFileName = HttpContext.Current.Server.MapPath(fontFileName);
					}
					PrivateFontCollection fonts = new PrivateFontCollection();
					fonts.AddFontFile(fontFileName);
					FontFamily family = fonts.Families[0];
					font = new Font(family, FontSize, FontStyle, FontSizeGraphicsUnit);
				}
				else
				{
					font = new Font(FontFamily, FontSize, FontStyle, FontSizeGraphicsUnit);
				}
			}

			if (ViewState["OutputImageType"] == null)
			{
				OutputImageType = ImageType.Gif;
			}

			if (string.IsNullOrEmpty(OutputDirectoryUrl))
			{
				OutputDirectoryUrl = DNAide.Configuration.DNAideConfigurationSettings.Web.CacheUrl + "/Images/Text";
			}

			SetFinalUrlAndPath();

			bool createBitmap = true;
			if (OutputWriteOption == ImageWriteOptionType.IgnoreIfExists)
			{
				if (File.Exists(FinalPath))
				{
					createBitmap = false;
				}
			}

			if (!createBitmap)
			{
				SetFinalWidthAndHeight();
				return;
			}

			if (this.ForeColorAlpha != 255)
			{
				ForeColor = Color.FromArgb(this.ForeColorAlpha, this.ForeColor);
			}

			if (this.BackColorAlpha != 255)
			{
				BackColor = Color.FromArgb(this.BackColorAlpha, this.BackColor);
			}

			BitmapToTransform = BitmapAide.CreateTextBitmap(this._textToRender, font, ForeColor, BackColor, MaxWidth, MaxHeight, StringFormat, ClipToText, GraphicsSettings, new Point(this.DrawXPosition, this.DrawYPosition));


			// Raise event allowing the developer to apply any transformations
			RaiseApplyTransformationsEvent();

			// Save the file
			SaveFile();

			// Set final width and height.
			SetFinalWidthAndHeight(BitmapToTransform.Width, BitmapToTransform.Height);

			DisposeImageToTransform();
		}


		#region Render Functions

		/// <summary>
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			if (!HasRunCreateOutput)
			{
				CreateOutput();
			}
			base.OnPreRender(e);
		}


		/// <summary>
		/// </summary>
		/// <param name="writer"></param>
		protected override void RenderStart(HtmlTextWriter writer)
		{
			if (!HasRunCreateOutput)
			{
				CreateOutput(); // Incase the control exists without a page
			}
			if (RenderType == TextImageRenderType.ClientScriptReplace)
			{
				writer.Write(_clientScriptReplaceHtml);
			}
			else
			{
				writer.WriteBeginTag("img");
			}
		}

		/// <summary>
		/// </summary>
		/// <param name="writer"></param>
		protected override void RenderEnd(HtmlTextWriter writer)
		{
			if (RenderType != TextImageRenderType.ClientScriptReplace)
			{
				writer.Write(" />");
			}
			else
			{
				writer.Write(string.Format(Resources.ImageText.ReplaceJavaScript, ClientID, this.FinalUrl, this.FinalWidth, this.FinalHeight));
			}
			DisposeImageToTransform();
		}


		/// <summary>
		/// </summary>
		/// <param name="writer"></param>
		protected override void RenderAttributes(HtmlTextWriter writer)
		{
			if (RenderType != TextImageRenderType.ClientScriptReplace)
			{
				this.Attributes.Remove("BackColor");
				this.Attributes.Remove("BackColorAlpha");
				this.Attributes.Remove("ClipToText");
				this.Attributes.Remove("DefaultOutputFileNameType");
				this.Attributes.Remove("DrawXPosition");
				this.Attributes.Remove("DrawYPosition");
				this.Attributes.Remove("Font");
				this.Attributes.Remove("FontFamily");
				this.Attributes.Remove("FontFileName");
				this.Attributes.Remove("FontSize");
				this.Attributes.Remove("FontSizeGraphicsUnit");
				this.Attributes.Remove("FontStyle");
				this.Attributes.Remove("ForeColor");
				this.Attributes.Remove("ForeColorAlpha");
				this.Attributes.Remove("GraphicsSettings");
				this.Attributes.Remove("MaxHeight");
				this.Attributes.Remove("MaxWidth");
				this.Attributes.Remove("RenderType");
				this.Attributes.Remove("StringFormat");
				this.Attributes.Remove("Text");
				base.RenderAttributes(writer);
			}
		}

		#endregion



	}
}
